package com.tiantian.system.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjUtil;
import com.google.common.base.Charsets;
import com.tiantian.common.core.constant.SystemConfigConstants;
import com.tiantian.common.core.domain.ResponseResult;
import com.tiantian.common.core.domain.dto.AnswerTimeDAO;
import com.tiantian.common.core.domain.model.SecondRoundInfo;
import com.tiantian.common.core.exception.BusinessException;
import com.tiantian.common.core.utils.DateUtils;
import com.tiantian.common.log.annotation.Log;
import com.tiantian.common.log.enums.BusinessType;
import com.tiantian.common.redis.utils.RedisUtils;
import com.tiantian.common.satoken.domain.controller.BaseController;
import com.tiantian.minio.utils.JxFileUtils;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.web.bind.annotation.*;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;


/**
 * 系统配置
 *
 * @author TianTian
 */
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/config")
@SaCheckPermission("setting")
@Slf4j
public class SystemConfigController extends BaseController {

    // 默认前缀(桶名称)
    @Value("${jx-minio.baseBucketName}")
    private String BASE_BUCKET_NAME;
    // 图床桶
    @Value("${jx-minio.drawingBedBucketName}")
    private String DRAWING_BED_BUCKET_NAME;
    private final DataSource dataSource;
    private final JxFileUtils jxFileUtils;

    /**
     * 重置系统
     */
    @PostMapping("/resetSystem")
    @SuppressWarnings("all")
    public ResponseResult<Void> resetSystem() {
        Long userId = getUserId();
        if (userId != 1L) {
            return ResponseResult.fail("重置系统失败, 权限不足");
        }
        Connection connection = null;
        Reader reader = null;
        try {
            // 重建数据库-执行 /jx-welcome/reset.sql
            connection = DataSourceUtils.getConnection(dataSource);
            // 创建脚本执行器
            ScriptRunner scriptRunner = new ScriptRunner(connection);
            // 创建字符输出流，用于记录SQL执行日志
            StringWriter writer = new StringWriter();
            PrintWriter print = new PrintWriter(writer);
            // 设置执行器日志输出和错误日志输出
            scriptRunner.setLogWriter(print);
            scriptRunner.setErrorLogWriter(print);
            String path = "/config/sql/reset.sql";
            // 获取资源文件的字符输入流
            reader = Resources.getResourceAsReader(path);
            log.debug("开始执行SQL脚本...");
            scriptRunner.runScript(reader);
            log.debug("SQL脚本执行完成.");
            // 清理minio中的所有文件
            jxFileUtils.removeObject(BASE_BUCKET_NAME,"/*");
            jxFileUtils.removeObject(DRAWING_BED_BUCKET_NAME,"/*");
            return ResponseResult.ok();
        } catch (IOException e) {
            log.error("执行SQL脚本时发生错误", e);
            return ResponseResult.fail("重置系统时发生错误");
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.error("关闭文件输入流时发生错误", e);
                }
            }
        }
    }


    /**
     * 设置答题时间
     *
     * @param answerTimeDAO 请求参数
     */
    @PostMapping("/setAnswerTime")
    @Log(businessType = BusinessType.ANS_TIME)
    public ResponseResult<Map<String, Object>> setAnswerTime(@RequestBody @Valid AnswerTimeDAO answerTimeDAO) {
        // 解析参数
        checkParameter(answerTimeDAO.getDate(), answerTimeDAO.getStart(), answerTimeDAO.getEnd());

        Map<String, Object> ajax = new HashMap<>();
        // 保存到Redis中
        RedisUtils.setCacheObject(SystemConfigConstants.SYSTEM_CONFIG_ANSWER_TIME_KEY, answerTimeDAO);
        ajax.put("isSetting", true);
        return ResponseResult.ok(ajax);
    }

    /**
     * 获取答题时间
     */
    @GetMapping("/getAnswerTime")
    @SaIgnore
    public ResponseResult<AnswerTimeDAO> getAnswerTime() {
        AnswerTimeDAO answerTimeDAOForRedis = RedisUtils.getCacheObject(SystemConfigConstants.SYSTEM_CONFIG_ANSWER_TIME_KEY);
        if (ObjUtil.isNotEmpty(answerTimeDAOForRedis)) {
            return ResponseResult.ok(answerTimeDAOForRedis);
        }
        return ResponseResult.fail("未设置答题时间");
    }

    /**
     * 更新注册开关
     */
    @PutMapping("/setRegStatus/{flag}")
    @Log(businessType = BusinessType.REGISTER_SETTING)
    public ResponseResult<Void> setRegStatus(@NotNull(message = "参数不能为空") @PathVariable Boolean flag) {
        RedisUtils.setCacheObject(SystemConfigConstants.SYSTEM_ENABLE_REGISTER_KEY, flag);
        return ResponseResult.ok();
    }

    /**
     * 获取注册开关的状态
     */
    @GetMapping("/getRegStatus")
    public ResponseResult<Boolean> getRegisterStatus() {
        Boolean o = RedisUtils.getCacheObject(SystemConfigConstants.SYSTEM_ENABLE_REGISTER_KEY);
        if (ObjUtil.isNull(o)) {
            // 默认不允许注册
            RedisUtils.setCacheObject(SystemConfigConstants.SYSTEM_ENABLE_REGISTER_KEY, false);
        }
        return ResponseResult.ok(o);
    }

    /**
     * 设置二轮招新时间地点
     */
    @PostMapping("/setSecondRoundInfo")
    public ResponseResult<Map<String, Object>> setSecondRoundInfo(@RequestBody @Valid SecondRoundInfo secondRoundInfo) {
        checkParameter(secondRoundInfo.getDate(), secondRoundInfo.getStart(), secondRoundInfo.getEnd());
        Map<String, Object> ajax = new HashMap<>();
        // 保存到Redis中
        RedisUtils.setCacheObject(SystemConfigConstants.SYSTEM_CONFIG_SECOND_ROUND_INFO_KEY, secondRoundInfo);
        ajax.put("isSetting", true);
        return ResponseResult.ok(ajax);
    }

    /**
     * 获取二轮招新时间地点
     */
    @GetMapping("/getSecondRoundInfo")
    public ResponseResult<SecondRoundInfo> getSecondRoundInfo() {
        SecondRoundInfo secondRoundInfoForRedis = RedisUtils.getCacheObject(SystemConfigConstants.SYSTEM_CONFIG_SECOND_ROUND_INFO_KEY);
        if (ObjUtil.isNotEmpty(secondRoundInfoForRedis)) {
            return ResponseResult.ok(secondRoundInfoForRedis);
        }
        return ResponseResult.fail("未设置二轮面试时间和地点");
    }

    @PostMapping("/setScoreLine")
    @Log(businessType = BusinessType.SCORE_LINE)
    @SaIgnore
    public ResponseResult<Void> setScoreLine(@NotNull(message = "最低分数线不能为空")
                                             @Max(message = "参数有错误", value = 100L)
                                             @Min(message = "参数有误", value = 5L)
                                             Integer scoreLine) {
        // 保存到Redis中
        HashMap<String, Integer> scoreLineMap = new HashMap<>();
        scoreLineMap.put("scoreLine", scoreLine);
        RedisUtils.setCacheObject(SystemConfigConstants.SYSTEM_CONFIG_INTERVIEW_SCORE_LINE, scoreLineMap);
        return ResponseResult.ok();
    }

    @GetMapping("/getScoreLine")
    @SuppressWarnings("all")
    public ResponseResult<Map<String, Integer>> getScoreLine() {
        Map<String, Integer> scoreLineMap = RedisUtils.getCacheObject(SystemConfigConstants.SYSTEM_CONFIG_INTERVIEW_SCORE_LINE);
        return ResponseResult.ok(scoreLineMap);
    }


    /**
     * 参数校验
     *
     * @param dateStr  日期      yyyy-MM-dd
     * @param startStr 开始时间  HH:mm:ss
     * @param endStr   结束时间  HH:mm:ss
     */
    private void checkParameter(String dateStr, String startStr, String endStr) {
        LocalDateTime date;
        LocalDateTime start;
        LocalDateTime end;
        // 解析参数
        try {
            date = LocalDateTimeUtil.parse(dateStr, DateUtils.YYYY_MM_DD);
            start = LocalDateTimeUtil.parse(startStr, DateUtils.HH_MM_SS);
            end = LocalDateTimeUtil.parse(endStr, DateUtils.HH_MM_SS);
        } catch (Exception e) {
            throw new BusinessException("时间格式错误,请重新输入");
        }

        // 判断时间是否符合规则
        Duration between = LocalDateTimeUtil.between(start, end);
        if (between.toMinutes() < 0) {
            throw new BusinessException("结束时间不能小于开始时间");
        }

        // 获取当前时间
        LocalDateTime localDateTimeNow = LocalDateTimeUtil.parse(
                LocalDateTimeUtil.format(LocalDateTime.now(), DateUtils.YYYY_MM_DD),
                DateUtils.YYYY_MM_DD);

        Duration betweenNow = LocalDateTimeUtil.between(localDateTimeNow, date);
        if (betweenNow.toMinutes() < 0) {
            throw new BusinessException("设置的答题日期不能小于当天");
        }
    }
}
